﻿using NPOI.SS.UserModel;
using System;
using System.ComponentModel;
using System.Diagnostics;

namespace XExcel.Models
{
    [DebuggerDisplay("ColumnIndex = {ColumnIndex}, ColumnName = {ColumnName}, Cell = {cell}")]
    class NpoiCell : IXCell
    {
        private readonly ICell cell;

        public int RowIndex => cell.RowIndex;

        public int ColumnIndex => cell.ColumnIndex;
        public string ColumnName { get; }
        internal NpoiCell(ICell cell)
        {
            this.cell = cell;
        }

        public NpoiCell(string columnName, ICell cell)
        {
            this.cell = cell;
            ColumnName = columnName;
        }

        public NpoiCell()
        {
        }

        public object GetValueOrError(Type type)
        {
            if (cell == null)
            {
                return type.GetNullOrError();
            }
            var cellType = cell.CellType;
            if (cellType == CellType.Formula)
            {
                cellType = cell.CachedFormulaResultType;
            }
            if (cellType == CellType.Blank)
            {
                return type.GetNullOrError();
            }
            if (cellType == CellType.Error || cell.CellType == CellType.Unknown)
            {
                throw new InvalidOperationException("单元格类型错误");
            }

            Type type1;
            Type type2 = type;
            Type type3 = !type2.IsGenericType || type2.GetGenericTypeDefinition() != typeof(Nullable<>) ? type2 : Nullable.GetUnderlyingType(type2);
            object v;

            if (cellType == CellType.Boolean)
            {
                v = cell.BooleanCellValue;
                type1 = v.GetType();
                if (type3 == type1)
                {
                    return v;
                }
            }
            else if (cellType == CellType.Numeric)
            {
                v = cell.NumericCellValue;
                type1 = v.GetType();
                if (IsNumericType(type3))
                {
                    if (type3 == type1)
                    {
                        return v;
                    }
                    if (type3 == typeof(decimal))
                    {
                        return Convert.ToDecimal(v);
                    }
                    return Convert.ChangeType(v, type3);
                }
            }
            else
            {
                v = cell.StringCellValue;
                type1 = typeof(string);
                if (type3 == type1)
                {
                    return v;
                }
            }
            TypeConverter converter = TypeDescriptor.GetConverter(type1);
            if (converter.CanConvertTo(type2))
            {
                return converter.ConvertTo(v, type2);
            }
            if (converter.CanConvertTo(type3))
            {
                return converter.ConvertTo(v, type3);
            }
            if (type3 == typeof(DateTime))
            {
                if (type1 == typeof(string) && DateTime.TryParse(v.ToString(), out DateTime result))
                {
                    return result;
                }
                if (converter.CanConvertTo(typeof(double)))
                {
                    return DateTime.FromOADate((double)converter.ConvertTo(v, typeof(double)));
                }
            }
            if (type3 == typeof(TimeSpan))
            {
                if (type1 == typeof(string) && TimeSpan.TryParse(v.ToString(), out TimeSpan result))
                {
                    return result;
                }
                if (converter.CanConvertTo(typeof(double)))
                {
                    return new TimeSpan(DateTime.FromOADate((double)converter.ConvertTo(v, typeof(double))).Ticks);
                }
            }

            if (string.IsNullOrWhiteSpace(v.ToString()))
            {
                return type.GetNullOrError();
            }

            var converter2 = TypeDescriptor.GetConverter(type3);
            if (converter2.CanConvertFrom(type1))
            {
                return converter2.ConvertFrom(v);
            }
            return type.GetNullOrError("值转换失败");
        }

        public object GetValue(Type type)
        {
            try
            {
                return GetValueOrError(type);
            }
            catch
            {
                return type.GetDefaultValue();
            }
        }
        public T GetValueOrError<T>()
        {
            return (T)GetValueOrError(typeof(T));
        }
        public T GetValue<T>()
        {
            return (T)GetValue(typeof(T));
        }
        private static bool IsNumericType(Type type)
        {
            return type == typeof(decimal) ||
                   type == typeof(double) ||
                   type == typeof(float) ||
                   type == typeof(long) ||
                   type == typeof(int) ||
                   type == typeof(byte) ||
                   type == typeof(ulong) ||
                   type == typeof(ushort) ||
                   type == typeof(short) ||
                   type == typeof(uint) ||
                   type == typeof(sbyte);
        }
    }
}